package com.kexilo.dict.autoconfigure;

import com.kexilo.dict.properties.DictProperties;
import com.kexilo.dict.service.DictService;
import com.kexilo.dict.service.DictServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.sql.DataSource;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 字典插件自动配置类
 * 根据条件自动装配字典相关组件
 * 
 * 包含组件：
 * - DictService: 字典服务接口及实现
 * - DictUtils: 字典工具类（从core-common移植）
 * - 缓存管理、预热、监控等功能
 * 
 * @author Kexilo
 */
@AutoConfiguration
@ConditionalOnClass({DictService.class})
@ConditionalOnProperty(value = "kexilo.plugin.dict.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(DictProperties.class)
@EnableAsync
public class DictAutoConfiguration {
    
    private static final Logger log = LoggerFactory.getLogger(DictAutoConfiguration.class);
    
    /**
     * 配置JdbcTemplate Bean
     * 复用admin模块的DataSource配置
     */
    @Bean
    @ConditionalOnClass({DataSource.class, JdbcTemplate.class})
    @ConditionalOnMissingBean(JdbcTemplate.class)
    @ConditionalOnProperty(value = "kexilo.plugin.dict.data-source-type", havingValue = "DATABASE", matchIfMissing = true)
    public JdbcTemplate dictJdbcTemplate(DataSource dataSource) {
        log.info("配置字典插件JdbcTemplate，复用admin模块DataSource");
        return new JdbcTemplate(dataSource);
    }
    
    /**
     * 配置本地缓存管理器
     * 当没有其他缓存管理器时使用
     */
    @Bean("dictCacheManager")
    @ConditionalOnMissingBean(name = "dictCacheManager")
    @ConditionalOnProperty(value = "kexilo.plugin.dict.cache-strategy", havingValue = "LOCAL", matchIfMissing = true)
    public CacheManager dictCacheManager(DictProperties properties) {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        cacheManager.setCacheNames(java.util.Collections.singletonList(properties.getCache().getCacheName()));
        cacheManager.setAllowNullValues(properties.getCache().isCacheNullValues());
        
        log.info("配置字典本地缓存管理器，缓存名称: {}", properties.getCache().getCacheName());
        return cacheManager;
    }
    
    /**
     * 配置DictService Bean
     */
    @Bean
    @ConditionalOnMissingBean(DictService.class)
    public DictService dictService(DictProperties properties,
                                 @Autowired(required = false) JdbcTemplate jdbcTemplate,
                                 @Autowired(required = false) CacheManager cacheManager) {
        
        // 使用字典专用缓存管理器，如果存在的话
        CacheManager dictCacheManager = cacheManager;
        try {
            // 尝试获取字典专用缓存管理器
            CacheManager specialCacheManager = (CacheManager) 
                org.springframework.util.ReflectionUtils.getField(
                    org.springframework.util.ReflectionUtils.findField(this.getClass(), "dictCacheManager"),
                    this);
            if (specialCacheManager != null) {
                dictCacheManager = specialCacheManager;
            }
        } catch (Exception e) {
            // 忽略，使用默认的缓存管理器
        }
        
        log.info("配置字典服务，缓存策略: {}, 数据源类型: {}", 
                properties.getCacheStrategy(), properties.getDataSourceType());
        return new DictServiceImpl(properties, jdbcTemplate, dictCacheManager);
    }
    
    /**
     * 配置异步任务执行器（专用于字典操作）
     */
    @Bean("dictTaskExecutor")
    @ConditionalOnProperty(value = "kexilo.plugin.dict.async-enabled", havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean(name = "dictTaskExecutor")
    public ThreadPoolTaskExecutor dictTaskExecutor(DictProperties properties) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("kexilo-dict-");
        executor.setKeepAliveSeconds(60);
        executor.setAllowCoreThreadTimeOut(false);
        
        // 拒绝策略：调用者运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 等待任务完成后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        
        executor.initialize();
        
        log.info("配置字典异步执行器，核心线程数: 2, 最大线程数: 5");
        return executor;
    }
    
    /**
     * 字典缓存预热配置
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.dict.warmup-enabled", havingValue = "true", matchIfMissing = true)
    public ApplicationRunner dictCacheWarmupRunner(DictService dictService, DictProperties properties) {
        return args -> {
            if (properties.isWarmupEnabled()) {
                log.info("开始字典缓存预热...");
                try {
                    dictService.warmupCache(properties.getWarmupTypes());
                    log.info("字典缓存预热完成");
                } catch (Exception e) {
                    log.warn("字典缓存预热失败", e);
                }
            }
        };
    }
    
    /**
     * 字典插件健康检查配置
     */
    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.health.HealthIndicator")
    @ConditionalOnProperty(value = "kexilo.plugin.dict.health.enabled", havingValue = "true")
    public DictHealthIndicator dictHealthIndicator(DictService dictService, DictProperties properties) {
        log.info("配置字典插件健康检查");
        return new DictHealthIndicator(dictService, properties);
    }
    
    /**
     * 字典插件性能监控配置
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.dict.monitor.enabled", havingValue = "true", matchIfMissing = true)
    public DictMonitorService dictMonitorService(DictService dictService, DictProperties properties) {
        log.info("配置字典插件性能监控");
        return new DictMonitorService(dictService, properties);
    }
    
    /**
     * 字典插件健康检查指示器
     */
    public static class DictHealthIndicator {
        
        private final DictService dictService;
        private final DictProperties properties;
        
        public DictHealthIndicator(DictService dictService, DictProperties properties) {
            this.dictService = dictService;
            this.properties = properties;
        }
        
        public String checkHealth() {
            try {
                if (!properties.isEnabled()) {
                    return "DISABLED";
                }
                
                boolean available = dictService.isAvailable();
                return available ? "UP" : "DOWN";
                
            } catch (Exception e) {
                return "DOWN: " + e.getMessage();
            }
        }
        
        public String getServiceInfo() {
            return String.format("DictService[%s] - CacheStrategy: %s, DataSourceType: %s, Async: %s", 
                    dictService.getServiceName(),
                    properties.getCacheStrategy(),
                    properties.getDataSourceType(),
                    properties.isAsyncEnabled());
        }
        
        public java.util.Map<String, Object> getDetailedHealth() {
            java.util.Map<String, Object> details = new java.util.HashMap<>();
            
            details.put("enabled", properties.isEnabled());
            details.put("cacheStrategy", properties.getCacheStrategy());
            details.put("dataSourceType", properties.getDataSourceType());
            details.put("asyncEnabled", properties.isAsyncEnabled());
            details.put("warmupEnabled", properties.isWarmupEnabled());
            details.put("available", dictService.isAvailable());
            
            if (properties.getMonitor().isCacheHitRateEnabled()) {
                details.put("cacheStats", dictService.getCacheStats());
            }
            
            return details;
        }
    }
    
    /**
     * 字典插件监控服务
     */
    public static class DictMonitorService {
        
        private final DictService dictService;
        private final DictProperties properties;
        private final Logger log = LoggerFactory.getLogger(DictMonitorService.class);
        
        public DictMonitorService(DictService dictService, DictProperties properties) {
            this.dictService = dictService;
            this.properties = properties;
        }
        
        /**
         * 监控缓存命中率
         */
        public void monitorCacheHitRate() {
            if (!properties.getMonitor().isCacheHitRateEnabled()) {
                return;
            }
            
            try {
                var stats = dictService.getCacheStats();
                Double hitRate = (Double) stats.get("hitRate");
                
                if (properties.getMonitor().isAlertEnabled() && 
                    hitRate < properties.getMonitor().getHitRateAlertThreshold()) {
                    log.warn("字典缓存命中率过低告警: {}, 阈值: {}", 
                            hitRate, properties.getMonitor().getHitRateAlertThreshold());
                }
                
                log.debug("字典缓存统计: {}", stats);
                
            } catch (Exception e) {
                log.error("监控缓存命中率失败", e);
            }
        }
        
        /**
         * 获取监控报告
         */
        public java.util.Map<String, Object> getMonitorReport() {
            java.util.Map<String, Object> report = new java.util.HashMap<>();
            
            try {
                report.put("serviceAvailable", dictService.isAvailable());
                report.put("cacheStats", dictService.getCacheStats());
                report.put("monitorConfig", java.util.Map.of(
                    "enabled", properties.getMonitor().isEnabled(),
                    "slowQueryThreshold", properties.getMonitor().getSlowQueryThreshold(),
                    "cacheHitRateEnabled", properties.getMonitor().isCacheHitRateEnabled(),
                    "alertEnabled", properties.getMonitor().isAlertEnabled()
                ));
                
            } catch (Exception e) {
                log.error("获取监控报告失败", e);
                report.put("error", e.getMessage());
            }
            
            return report;
        }
    }
}
